package cn.siat.gene.service;

import cn.hutool.core.io.file.FileReader;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.util.IdUtil;
import cn.siat.common.core.exception.ServiceException;
import cn.siat.common.core.utils.StringUtils;
import cn.siat.common.core.web.domain.AjaxResult;
import cn.siat.common.security.utils.SecurityUtils;
import cn.siat.gene.constant.OperateEnum;
import cn.siat.gene.domain.Feature;
import cn.siat.gene.domain.Programme;
import cn.siat.gene.domain.Sequence;
import cn.siat.gene.domain.SequenceOperate;
import cn.siat.gene.mapper.FeatureMapper;
import cn.siat.gene.mapper.ProgrammeMapper;
import cn.siat.gene.mapper.SequenceMapper;
import cn.siat.gene.mapper.SequenceOperateMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
@RequiredArgsConstructor
@Slf4j
public class SequenceOperateService {
    private final SequenceOperateMapper sequenceOperateMapper;
    private final SequenceMapper sequenceMapper;
    private final ProgrammeMapper programmeMapper;
    private final FeatureService featureService;
    private final FeatureMapper featureMapper;
    @Value("${path.sequence}")
    private String sequencePath;
    public List<SequenceOperate> listSequenceOperate(SequenceOperate sequenceOperate){
        // 显示的end的要-1
        List<SequenceOperate> list =  sequenceOperateMapper.list(sequenceOperate);
        for (SequenceOperate operate : list) {
            if(operate.getEnd()!=null){
                operate.setEnd(operate.getEnd()-1);
            }
        }
        return list;
    }

    /**
     * 历史文件导入检查是否正确
     * @param sequenceOperateList
     * @return
     */
    public boolean checkOperateConfig(List<SequenceOperate> sequenceOperateList){
        long i = 0;
        for (SequenceOperate sequenceOperate : sequenceOperateList) {
            if(sequenceOperate.getId()==null){
                sequenceOperate.setId(i++);
            }
        }
        for (SequenceOperate a : sequenceOperateList) {
            for (SequenceOperate b : sequenceOperateList) {
                if(!a.equals(b)){
                    // 环形
                    if(a.getStart()>a.getEnd()){
                        if(a.getStart()<b.getEnd()||a.getEnd()>b.getStart()){
                            return false;
                        }
                    }else if(b.getStart()>b.getEnd()){
                        if(b.getStart()<a.getEnd()||b.getEnd()>a.getStart()){
                            return false;
                        }
                    }else{
                        // 非环形
                        if(!(a.getEnd()<b.getStart()||a.getStart()>b.getEnd())){
                            return false;
                        }
                    }
                }
            }
        }
        return true;
    }
    public List<SequenceOperate> convertOperateConfig(List<SequenceOperate> sequenceOperateList){
        Collections.reverse(sequenceOperateList);
        List<SequenceOperate> result = new ArrayList<>();
        for (SequenceOperate sequenceOperate : sequenceOperateList) {
            SequenceOperate obj = new SequenceOperate();
            BeanUtils.copyProperties(sequenceOperate,obj);
            for (SequenceOperate operate : result) {
                int start = obj.getStart();
                int end = obj.getEnd();
                if(OperateEnum.CUT.getValue().equalsIgnoreCase(operate.getType())){
                    if(start>end){
                        int length = operate.getEnd()-operate.getStart();
                        obj.setStart(start-length);
                    }else if(operate.getStart()>operate.getEnd()){
                        obj.setStart(start-operate.getEnd());
                        obj.setEnd(end-operate.getEnd());
                    }else if(start>=operate.getStart()){
                        int length = operate.getEnd()-operate.getStart();
                        obj.setStart(start-length);
                        obj.setEnd(end-length);
                    }
                }else if(OperateEnum.INSERT.getValue().equalsIgnoreCase(operate.getType())){
                    int length = operate.getSequence().length();
                    if(start>end){
                        obj.setStart(start+length);
                    } else if(start>=operate.getStart()){
                        obj.setStart(start+length);
                        obj.setEnd(end+length);
                    }
                }else{
                    int length = operate.getEnd() - operate.getStart() - operate.getSequence().length();
                    if(start>end){
                        obj.setStart(start+length);
                    } else if(operate.getStart()>operate.getEnd()){
                        // replace替换的会在0处插入
                        length = operate.getEnd()-operate.getSequence().length();
                        obj.setStart(start-length);
                        obj.setEnd(end-length);
                    }else if(start>=operate.getStart()){
                        obj.setStart(start-length);
                        obj.setEnd(end-length);
                    }
                }
            }
//            System.out.println(obj.getStart()+":"+obj.getEnd());
            result.add(obj);
        }
        return result;
    }
    /**
     * 检查本次序列是否可以操作
     * @param sequenceOperate
     */
    public boolean checkCanOperate(SequenceOperate sequenceOperate){
        sequenceOperate.setId(IdUtil.getSnowflakeNextId());
        List<SequenceOperate> sourceList = this.listOperateSource(sequenceOperate.getSequenceId(),sequenceOperate);
        for (SequenceOperate operate : sourceList) {
            // 存的值不含尾巴,检验的时候含有,要-1
            operate.setEnd(operate.getEnd()-1);
        }
        return this.checkOperateConfig(sourceList);
//        for (SequenceOperate operate : currentList) {
//            if(newStart>newEnd && operate.getStart()>operate.getEnd()){
//                log.error("同时只能有一个环形操作");
//                return false;
//            }
//            if(OperateEnum.INSERT.getValue().equals(operate.getType())){
//                if(operate.getStart()<operate.getEnd()){
//                    // 原来的位点的结尾要按序列长度计算
//                    int end = operate.getEnd()-operate.getStart()+operate.getSequence().length();
//                    int start = operate.getStart();
//                    if(!(newEnd<start||newStart>end)){
//                        return false;
//                    }
//                }else{
//                    if(newEnd>operate.getStart()||newStart<operate.getEnd()){
//                        return false;
//                    }
//                }
//            }else if(OperateEnum.REPLACE.getValue().equals(operate.getType())){
//                int end = operate.getStart()+operate.getSequence().length();
//                int start = operate.getStart();
//                if(newStart>newEnd){
//                    if(newStart<end||newEnd>start){
//                        return false;
//                    }
//                }else if(operate.getStart()<operate.getEnd()){
//                    // 原来的位点的结尾要按序列长度计算
//
//                    if(!(newEnd<start||newStart>end)){
//                        return false;
//                    }
//                }else{
//                    if(newEnd>operate.getStart()||newStart<Math.abs(operate.getEnd()-operate.getSequence().length())){
//                        return false;
//                    }
//                }
//            }else{
//                if(newStart>newEnd){
//                    if(newStart< operate.getEnd()||newEnd>operate.getStart()){
//                        return false;
//                    }
//                }else if(operate.getStart()<operate.getEnd()){
//                    if(!(newEnd<operate.getStart()||newStart>operate.getEnd())){
//                        return false;
//                    }
//                }else{
//                    if(newEnd>operate.getStart()||newStart<operate.getEnd()){
//                        return false;
//                    }
//                }
//            }
//        }
//        return true;
    }


    /**
     * 将坐标转换为原始位点
     * @param sequenceId
     */
    public List<SequenceOperate> listOperateSource(Long sequenceId, SequenceOperate newSequenceOperate){
        List<SequenceOperate> list =  sequenceOperateMapper.list(new SequenceOperate().setSequenceId(sequenceId));
        if(newSequenceOperate!=null){
            list.add(0,newSequenceOperate);
        }
        List<SequenceOperate> result = new ArrayList<>(list.size());
        for (SequenceOperate sequenceOperate : list) {
            result.add(convertOperateSource(list,sequenceOperate));
        }
        return result;
    }

    /**
     * 获取指定操作的原始位点,需要sequenceId和operateId
     * @param currentSequenceOperate
     * @return
     */
    public SequenceOperate getOperateSource(SequenceOperate currentSequenceOperate){
        List<SequenceOperate> list =  sequenceOperateMapper.list(new SequenceOperate().setSequenceId(currentSequenceOperate.getSequenceId()));
        return convertOperateSource(list,currentSequenceOperate);
    }
    /**
     * 返回原始坐标的操作对象
     * @param list
     * @param currentOperate
     * @return
     */
    private SequenceOperate convertOperateSource(List<SequenceOperate> list,SequenceOperate currentOperate){
        int currentStart = currentOperate.getStart();
        int currentEnd = currentOperate.getEnd();
        for (SequenceOperate obj : list) {
            if(obj.getId()>= currentOperate.getId()){
                continue;
            }
            // 逐级操作,此时认定数据都是准确的，不存在区域交叉的情况
            if(OperateEnum.CUT.getValue().equalsIgnoreCase(obj.getType())){
                int length = obj.getPreSequence().length();
                if(obj.getStart()>obj.getEnd()){
                    currentStart = currentStart+obj.getEnd();
                    currentEnd = currentStart+obj.getEnd();
                }else if(currentStart>currentEnd){
                    currentStart = currentStart+obj.getEnd();
                }else if(currentStart>=obj.getStart()){
                    // 被删除的要还回来
                    currentStart = currentStart+length;
                    currentEnd = currentEnd+ length;
                }
            }else if(OperateEnum.INSERT.getValue().equalsIgnoreCase(obj.getType())){
                int length = obj.getSequence().length();
                // 多插入的要删除
                if(currentStart>currentEnd){
                    currentStart = currentStart-length;
                }else if(currentStart>=obj.getStart()){
                    if(currentStart<obj.getStart()+length){
                        throw new ServiceException("该区间不可以操作");
                    }
                    currentStart = currentStart-length;
                    currentEnd = currentEnd - length;
                }
            }else{
                // replace 计算一下长短
                if(obj.getStart()>obj.getEnd()){
                    currentStart = currentStart + obj.getEnd()-obj.getSequence().length();
                    currentEnd = currentEnd+ obj.getEnd()-obj.getSequence().length();
                }else if(currentStart>currentEnd){
                    currentStart = currentStart+obj.getEnd() - obj.getStart() - obj.getSequence().length();
                }else if(currentStart>=obj.getStart()){
                    int range = obj.getEnd() - obj.getStart() - obj.getSequence().length();
                    currentStart = currentStart + range;
                    currentEnd = currentEnd + range;
                }
            }
        }
        SequenceOperate targetOperate = new SequenceOperate();
        BeanUtils.copyProperties(currentOperate,targetOperate);
        targetOperate.setStart(currentStart).setEnd(currentEnd);
//        System.out.println(currentStart+":"+currentEnd);
        return targetOperate;
    }
    public AjaxResult parseFromTxt(String filePath){
        FileReader fileReader = new FileReader(filePath);
        List<String> lineList = fileReader.readLines();
        List<SequenceOperate> sequenceOperateList = new ArrayList<>();
        // sequenceName|start|end|type|sequence|preSequence|order
        for (String s : lineList) {
            String[] strings = s.split("\t");
            if(strings.length<7){
                return AjaxResult.error("历史记录缺少参数");
            }
            SequenceOperate sequenceOperate = new SequenceOperate();
            sequenceOperate.setSequenceName(strings[0].trim());
            String start = strings[2].trim(),end=strings[3].trim();
            if(StringUtils.isEmpty(end)||"-".equals(end)){
                end = start;
            }
            String regex = "^\\d+$";
            if(!start.matches(regex)||!end.matches(regex)){
                return AjaxResult.error("数字类型转化失败");
            }
            sequenceOperate.setStart(Integer.valueOf(start)).setEnd(Integer.valueOf(end));
            String type = strings[4].trim();
            if(!OperateEnum.CUT.getValue().equalsIgnoreCase(type) && !OperateEnum.INSERT.getValue().equalsIgnoreCase(type)
                    && !OperateEnum.REPLACE.getValue().equalsIgnoreCase(type)){
                return AjaxResult.error("历史记录无此操作标识");
            }
            sequenceOperate.setType(type);
            sequenceOperate.setSequence(strings[5]);
            sequenceOperateList.add(sequenceOperate);
        }
        return AjaxResult.success("操作成功",sequenceOperateList);
    }
//    @Transactional(rollbackFor = Exception.class)
//    public AjaxResult saveList(List<SequenceOperate> list,Long sequenceId){
//        String userId = SecurityUtils.getUserId();
//        String nickName = SecurityUtils.getLoginUser().getSysUser().getNickName();
//        List<SequenceOperate> operateList = new ArrayList<>();
//        Integer maxOrder = sequenceOperateMapper.countMaxOrder(sequenceId);
//        if(maxOrder==null){
//            maxOrder = 0;
//        }
//        maxOrder+=list.size();
//        for (SequenceOperate sequenceOperate : list) {
//            sequenceOperate.setCreateBy(userId).setCreateByName(nickName).setSequenceId(sequenceId);
//            if(sequenceOperate.getOrder()==null){
//                sequenceOperate.setOrder(maxOrder);
//            }
//            maxOrder--;
//            operateList.add(sequenceOperate);
//            if(operateList.size()==2000){
//                sequenceOperateMapper.saveList(operateList);
//                operateList.clear();
//            }
//        }
//        if(!operateList.isEmpty()){
//            sequenceOperateMapper.saveList(operateList);
//        }
//
//        return AjaxResult.success("操作成功");
//    }
    public AjaxResult save(SequenceOperate sequenceOperate){
        String userId = SecurityUtils.getUserId();
        String nickName = SecurityUtils.getLoginUser().getSysUser().getNickName();
        sequenceOperate.setCreateBy(userId).setCreateByName(nickName);
        Integer maxOrder = sequenceOperateMapper.countMaxOrder(sequenceOperate.getSequenceId());
        if(maxOrder==null){
            maxOrder = 0;
        }
        sequenceOperate.setOrder(maxOrder+1);
        sequenceOperateMapper.save(sequenceOperate);
        return AjaxResult.success("操作成功");
    }
    public AjaxResult removeList(List<SequenceOperate> list,Long sequenceId){
        List<Long> idList = new ArrayList<>();
        for (SequenceOperate sequenceOperate : list) {
            idList.add(sequenceOperate.getId());
            if(idList.size()>2000){
                sequenceOperateMapper.removeByIdList(sequenceId,idList);
                idList.clear();
            }
        }
        if(!idList.isEmpty()){
            sequenceOperateMapper.removeByIdList(sequenceId,idList);
            idList.clear();
        }
        return AjaxResult.success("操作成功");
    }

    /**
     * 多步回滚
     * @param sequenceId
     * @param id
     * @return
     */
    public AjaxResult rollback(Long sequenceId,Long id){
        List<SequenceOperate> sourceList = sequenceOperateMapper.listRollback(new SequenceOperate().setSequenceId(sequenceId).setId(id));
        List<SequenceOperate> targetList = new ArrayList<>(sourceList.size());
        for (SequenceOperate obj : sourceList) {
            if(Boolean.FALSE.equals(obj.getCanRollback())){
                return AjaxResult.error("不可操作");
            }
            SequenceOperate delObj = new SequenceOperate();
            delObj.setStart(obj.getStart());
            if(OperateEnum.CUT.getValue().equalsIgnoreCase(obj.getType())){
                delObj.setType(OperateEnum.INSERT.getValue());
                delObj.setEnd(obj.getEnd());
                delObj.setSequence(obj.getPreSequence());
            }else if(OperateEnum.INSERT.getValue().equalsIgnoreCase(obj.getType())){
                delObj.setType(OperateEnum.CUT.getValue());
                delObj.setEnd(delObj.getStart()+obj.getSequence().length());
            }else{
                delObj.setType(OperateEnum.REPLACE.getValue());
                delObj.setSequence(obj.getPreSequence());
                delObj.setEnd(obj.getEnd());
                delObj.setPreSequence(obj.getSequence());
            }
            targetList.add(delObj);
        }
        SequenceOperate sequenceOperate = sourceList.get(sourceList.size()-1);
        featureService.rollBack(sequenceId,sequenceOperate.getId());
        removeList(sourceList,sequenceId);
        operateSequenceFile(targetList,sequenceId,true);
        return AjaxResult.success("操作成功");
    }
    /**
     *  操作记录,start,end都是从1开始的,end始终比start大,含头不含尾
     * @param list
     * @param sequenceId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult operateSequenceFile(List<SequenceOperate> list,Long sequenceId,boolean isRollBack){
        Sequence sequence = sequenceMapper.get(new Sequence().setId(sequenceId));
        String sequenceFolder = getSequenceFolderPath(sequence.getProgrammeId());
        FileReader fileReader = new FileReader(sequenceFolder+sequence.getFileName());
        StringBuilder sb = new StringBuilder(fileReader.readString());
        int oldSequenceLength = sb.length();
        if(list.size()>0){
            List<Feature> features = featureMapper.list(new Feature().setSequenceId(sequence.getId()));
            featureService.cacheFeatureList(sequenceId,features);
        }
        boolean isFinal = false;
        // 操作文件,更改feature
        for (int i = 0; i < list.size(); i++) {
            if(i==list.size()-1){
                isFinal = true;
            }
            SequenceOperate sequenceOperate = list.get(i);
            int start = sequenceOperate.getStart()-1;
            int end = sequenceOperate.getEnd()-1;
            if(start==end){
                end++;
            }
            Feature param = new Feature();
            param.setSequenceId(sequenceId);
            param.setStart(start).setEnd(end);
            param.setOldSequenceLength(oldSequenceLength);
            OperateEnum operateEnum = OperateEnum.CUT;
            //剪切
            if(OperateEnum.CUT.getValue().equalsIgnoreCase(sequenceOperate.getType())){
                if(Boolean.TRUE.equals(sequenceOperate.getNeedSetPreSequence())){
                    if(start>end){
                        String preS = sb.substring(0,end)+sb.substring(start,sb.length());
                        sequenceOperate.setPreSequence(preS);
                    }else{
                        sequenceOperate.setPreSequence(sb.substring(start, end));
                    }
                }
                if(start>end){
                    // 环形分两端剪切
                    sb.delete(start,sb.length());
                    sb.delete(0,end);
                }else{
                    sb.delete(start,end);
                }
                operateEnum = OperateEnum.CUT;
            }else if(OperateEnum.INSERT.getValue().equalsIgnoreCase(sequenceOperate.getType())){
                if(isRollBack && start>end){
                    String insertSequence = sequenceOperate.getSequence().substring(0,end);
                    String insertSequenceA = sequenceOperate.getSequence().substring(end);
                    sb.insert(0,insertSequence);
                    sb.append(insertSequenceA);
                }else{
                    // 插入
                    sb.insert(start,sequenceOperate.getSequence());
                    if(!isRollBack) {
                        operateEnum = OperateEnum.INSERT;
                        param.setEnd(param.getStart()+sequenceOperate.getSequence().length());
                        param.setSequenceLength(sequenceOperate.getSequence().length());
                    }
                }

            }else{
                if(isRollBack && start>end) {
                    String insertSequence = sequenceOperate.getSequence().substring(0, end);
                    String insertSequenceA = sequenceOperate.getSequence().substring(end);
                    sb.delete(0,sequenceOperate.getPreSequence().length());
                    sb.insert(0, insertSequence);
                    sb.append(insertSequenceA);
                }else{
                    // 替换
                    if(Boolean.TRUE.equals(sequenceOperate.getNeedSetPreSequence())) {
                        if(start>end){
                            String preS = sb.substring(0,end)+sb.substring(start,sb.length());
                            sequenceOperate.setPreSequence(preS);
                        }else{
                            sequenceOperate.setPreSequence(sb.substring(start, end));
                        }

                    }
                    if(start>end){
                        sb.delete(start,sb.length());
                        sb.replace(0,end,sequenceOperate.getSequence());
                    }else{
                        sb.replace(start,end,sequenceOperate.getSequence());
                    }
                    if(!isRollBack) {
                        operateEnum = OperateEnum.REPLACE;
                        // 差值
                        param.setSequenceLength(sequenceOperate.getSequence().length());
                    }
                }

            }
            if(!isRollBack){
                this.save(sequenceOperate);
                featureService.changeFeatureIndex(param,operateEnum,sequenceOperate.getId(),isFinal);
            }
        }
        FileWriter fw = new FileWriter(sequenceFolder+sequence.getFileName());
        String str = sb.toString();
        sequenceMapper.update(new Sequence().setId(sequenceId).setBpLength(str.length()));
        Programme up =new Programme().setId(sequence.getProgrammeId()).setUpdateTime(new Date());
        up.setUpdateStaffNo(SecurityUtils.getUserId());
        programmeMapper.updateById(up);
        fw.write(str);
        return AjaxResult.success();
    }
    public void removeBySequenceIdList(List<Long> idList){
        List<Long> removeList = new ArrayList<>(2000);
        // 2k一删除
        Iterator<Long> iterable = idList.iterator();
        while (iterable.hasNext()){
            removeList.add(iterable.next());
            if(removeList.size()>2000||!iterable.hasNext()){
                sequenceOperateMapper.removeBySequenceIdList(removeList);
                removeList.clear();
            }
        }
    }
    private String getSequenceFolderPath(String programmeId){
        return sequencePath +"/"+programmeId+"/";
    }
}
